﻿
#include <stdio.h>

#include <event2/util.h>
#include <event2/thread.h>

#ifdef WIN32
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
#include <windows.h>
#endif

#include <thread>
#include <functional>

#include "SLT_timer_imp.h"

static void libevent_on_timeout_20220301(evutil_socket_t fd, short event, void* arg) {
	LibEventTimerContext* p = (LibEventTimerContext*)arg;

	p->cb(SLT_timerDue, p->timerid, p->prama);
	/*if (!p->periodic) {
		delete p;
	}*/
	delete p;
}
static void setTimer_cb_20220301(evutil_socket_t fd, short event, void* arg)
{
	LibeventTimer* timer = (LibeventTimer*)arg;
	timer->_setTimer_cb(fd, event);

}
void LibeventTimer::_setTimer_cb(evutil_socket_t fd, short event)
{
	if (EV_CLOSED & event) {
		printf("\ntimer:socket close with success\n");
	}

	if (EV_READ & event) {
		char buf[8] = { 0 };
		int recvlen = recv(fd, buf, sizeof(buf), 0);
		if (recvlen < 0) {
			printf("\n timer recv:%d\n", recvlen);
		}
		else if (recvlen == 0) {
			printf("\n timer recv:%d \n", recvlen);
		}
		else {
			while (1) {
				m_baseLock.lock();
				if (m_setTimerlist.empty()) {
					m_setRun = false;
					m_baseLock.unlock();
					break;
				}

				for (int j = 0; j < 10 && !m_setTimerlist.empty(); j++) {
					m_listtemp.push_back(m_setTimerlist.front());
					m_setTimerlist.pop_front();
				}
				m_baseLock.unlock();

				while (!m_listtemp.empty()) {
					LibEventTimerContext* ctx = m_listtemp.front();
					m_listtemp.pop_front();

					if (0 != event_assign(&ctx->m_ev, m_base, -1, 0, libevent_on_timeout_20220301, (void*)ctx)) {
						delete ctx;
					}
					else {
						if (0 != evtimer_add(&ctx->m_ev, &ctx->etime)) {
							printf("timer add error\n");
						}
					}
				}
			}
		}
	}
}

void SLT_libeventInit()
{
	static std::once_flag onceflag;
	std::call_once(onceflag, []() {
#ifdef WIN32
		WORD wVersionRequested;
		WSADATA wsaData;
		wVersionRequested = MAKEWORD(2, 2);
		WSAStartup(wVersionRequested, &wsaData);

		evthread_use_windows_threads();
#else
		evthread_use_pthreads();
#endif
		});
}

void LibeventTimer::init()
{
	SLT_libeventInit();
}

void LibeventTimer::loop_proc(void*) 
{
	event_base_loop(m_base, EVLOOP_NO_EXIT_ON_EMPTY);
}

bool LibeventTimer::start(){
    m_base = event_base_new();
    if(NULL == m_base){
        return false;
    }
	if (evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
		printf("evutil_socketpair error\n");
		event_base_free(m_base);
		return false;
	}
	struct event* ev = event_new(m_base, pair[1], EV_CLOSED | EV_READ | EV_PERSIST, setTimer_cb_20220301, this);
	event_add(ev, 0);

    //不支持停止
    std::thread thread(std::bind(&LibeventTimer::loop_proc, this, std::placeholders::_1),nullptr);
	//std::thread thread(timer_loop_proc, this);
    thread.detach();
    return true;
}

int LibeventTimer::set_timer(uint64_t &_th,uint32_t elapseMs, bool periodic, SLT_libeventTimerTimeOut cb, void* prama)
{
	//periodic 没有实现.需要和cancel同时实现
#if 0
	LibEventTimerContext* ctx = new LibEventTimerContext();
	ctx->cb = cb;
	ctx->periodic = periodic;
	ctx->prama = prama;
	ctx->timerid = m_timerid_seed++;
	ctx->pTimer = this;

	_th = ctx->timerid;

	ctx->etime.tv_sec = elapseMs / 1000;
	ctx->etime.tv_usec = elapseMs % 1000 * 1000;

	m_baseLock.lock();
	m_setTimerlist.push_back(ctx);
	if (m_setRun == false) {
		m_setRun = true;
		if (send(pair[0], "a", 1, 0) < 0) {
			printf("\ntimer send error\n");
			m_setRun = false;
			_th = 0;

			m_baseLock.unlock();
			return -1;
		}
	}
	m_baseLock.unlock();
	//linux 版本的evthread_use_pthreads 没有效果，多线程evtimer_add 奔溃
#else

	LibEventTimerContext* ctx = new LibEventTimerContext();
	int flag = 0;
	//int flag = EV_FINALIZE;
	//if (periodic) {
	//	flag |= EV_PERSIST;
	//}

	if (0 != event_assign(&ctx->m_ev, m_base, -1, flag, libevent_on_timeout_20220301, (void*)ctx)) {
		delete ctx;
		return -2;
	}

	ctx->cb = cb;
	ctx->periodic = periodic;
	ctx->prama = prama;
	ctx->timerid = m_timerid_seed++;
	ctx->pTimer = this;

	_th = ctx->timerid;

	timeval escape_time;
	escape_time.tv_sec = elapseMs / 1000;
	escape_time.tv_usec = elapseMs % 1000 * 1000;

	if (0 != evtimer_add(&ctx->m_ev, &escape_time)) {
		_th = 0;
		return -1;
	}
#endif
	return 0;
}
